


#include <vector>
#include <string>
#include <stdlib.h>
#include <iostream>
#include <getopt.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <vector>
#include <limits.h>
#include <iterator>
#include <fstream>

#include "split.h"
#include "position.h"
#include "sum.h"
#include "output.h"


using namespace std;

const char* PROGRAM_NAME = "keysum";


/*
 -k  --key    key field index. only one.eg --key=4
 -c  --value  value field array. eg --value=1,2,3
 -t  --type   sum,max,min,avg
 -i  --infile input file, not standard out
 -o  --outfile output file, not standard out
     --html  
     --csv    
 -d  --delimiter
*/
void
Usage(int status)
{
	if(status != 0)
	{
		fprintf(stderr, "Try `%s --help` for more informaton.\n", PROGRAM_NAME);
	}
	else
	{
		printf("Usage: %s [OPTION]\n", PROGRAM_NAME);
		fputs("keysum application\n", stdout);
		fputs("\
  -k,  --key=NUM	key field index. only one field.\n\
  -c,  --val=LIST	value field index list. eg --val=1,2,3\n\
  -t,  --type       	sum,max,min,avg.\n\
  -i,  --infile		use input file, not stdout.\n\
  -d,  --delimiter      use DELIM instead of TAB for field delimiter\n\
  -o,  --outfile	use output file, not stdout.\n\
       --html/csv       output format.\n\
  -h,  --help 		print the information.\n\
  -v,  --version        print the version.\n",stdout);
	}
	exit(status);
}


enum {
	HTML_OPTION = CHAR_MAX + 1,
	CSV_OPTION 
};

static struct option const long_options[] = 
{
	{"key",		required_argument,	NULL,	'k'},
	{"val",		required_argument,	NULL, 	'c'},
	{"type",	optional_argument,	NULL, 	't'},
	{"infile",	optional_argument,	NULL,	'i'},
 	{"delimiter",	optional_argument,	NULL,	'd'},
	{"outfile",	optional_argument,	NULL,	'o'},
	{"help",	no_argument,		NULL,   'h'},
	{"html",	no_argument,		NULL,	HTML_OPTION},
	{"csv",		no_argument,		NULL, 	CSV_OPTION},
  	{NULL, 0, NULL, 0}
};


static void ParseValueIndex(const string str, vector<int> &vect)
{
	int start = 0;
	int end = 0;
	
	if(str.empty())
		return;
	
	while(true)
	{
		end = str.find_first_of(",",start);
		if(string::npos == end)
		{
			string tmp = str.substr(start);
			vect.push_back(strtoul(tmp.c_str(), NULL, 10));
			break;
		}
		else
		{
			string tmp = str.substr(start, end-start);
			vect.push_back(strtoul(tmp.c_str(), NULL, 10));
			start = end + 1;
		}
	}
}

#if 0
enum CALC_TYPE
{
	CALC_SUM = 1,
	CALC_MAX = 2,
	CALC_MIN = 3,
	CALC_AVG = 4
};
#endif

static void ParseCalcType(const string str, CALC_TYPE &type)
{
	if(str == "max")
		type = CALC_MAX;
	else if(str == "min")
		type = CALC_MIN;
	else if(str == "avg")
		type = CALC_AVG;
	else
		type = CALC_SUM;
}

enum OUTPUT_TYPE
{
	OUTPUT_STDOUT = 1,
	OUTPUT_HTML,
	OUTPUT_CSV
};

static OUTPUT_TYPE s_output_type = OUTPUT_STDOUT;
static CALC_TYPE   s_calc_type = CALC_SUM;
static int s_nkeyindex = 1;
static vector<int>	s_vectvalindex;
static string  s_infile = "";
static string  s_outfile = "";
static string  s_delimiter = " \t";

int main(int argc, char* argv[])
{
	int c;
	while((c = getopt_long(argc, argv, "k:c:t:i:o:hd:", long_options, NULL)) != -1)
	{
		switch(c)
		{
			case 0:
				break;
			case 'k':
				s_nkeyindex = atoi(optarg);
				break;
			case 'c':
				ParseValueIndex(optarg, s_vectvalindex);	
				break;
			case 't':
				ParseCalcType(optarg, s_calc_type);	
				break;
			case 'i':
				s_infile = optarg;
				break;
			case 'o':
				s_outfile = optarg;
				break;
			case 'd':
				s_delimiter = optarg;
				break;
			case HTML_OPTION:
				s_output_type = OUTPUT_HTML;
				break;
			case CSV_OPTION:
				s_output_type = OUTPUT_CSV;
				break;
			case 'h':
			default:
				Usage(0);
		}
	}


	if(s_vectvalindex.empty())
		Usage(1);

	//printf("key(%d),delim(%s)\n", s_nkeyindex, s_delimiter.c_str());
	//copy(s_vectvalindex.begin(), s_vectvalindex.end(), ostream_iterator<int>(cout, ","));	

	Position  pos;
	for(size_t i = 0; i < s_vectvalindex.size(); ++i)
	{
		pos.add(s_vectvalindex[i]);
		if(s_nkeyindex == s_vectvalindex[i])
			Usage(2);
	}

	DataSum ds(pos, s_calc_type);
	Split sp(&ds, s_nkeyindex, s_delimiter);
	/******************************************************/
	char buf[4096] = {0};
	if(s_infile.empty())
	{
		while(cin.getline(buf, 4096-1, '\n'))
		{
			sp.SplitLine(buf);
			memset(buf, 4096, 0);
		}
	}
	else
	{
		//fstream fin(s_infile);
		ifstream fin;
		fin.open(s_infile.c_str());
		while(fin.getline(buf, 4096-1))
		{
			sp.SplitLine(buf);
			memset(buf, 4096, 0);
		}
		fin.close();
	}
	/******************************************************/

	Output *poutput = NULL;
	if(OUTPUT_HTML == s_output_type)
		poutput = new HTMLOutput(pos.size(), s_outfile);
	else if(OUTPUT_CSV == s_output_type)
		poutput = new CSVOutput(pos.size(), s_outfile);
	else 
		poutput = new Output(pos.size(), s_outfile);

	//ds.flush(*poutput);
	poutput->FlushData2Stream(ds);

	delete poutput;

	return 0;
}

